#ifndef __GlSpriteRenderer_h__
#define __GlSpriteRenderer_h__

#include <d3d11.h>
#include <vector>

namespace GraphicLibrary
{
	class DxGraphicComponent;
	class DxSprite;
	class DxSpotLight;
	class Size;

	class SpriteRenderer
	{
		public:
			enum ExceptionList
			{
				ElUnableToCompileShaderPrograms,
				ElUnableToCreateInputLayout,
				ElUnableToCreateSamplerState,
				ElUnableToUpdateBufferData
			};
			~SpriteRenderer();
			void tick(const float deltaTime);
			void add(DxGraphicComponent *s);
			void remove(DxGraphicComponent *s);
			void renderAll(ID3D11DeviceContext *ic) const;

			inline static void Init(ID3D11Device *device, const Size &screenSize)
			{
				if(msSingleton)
					delete msSingleton;
				msSingleton = new SpriteRenderer(device, screenSize);
			}
			inline static SpriteRenderer* GetSingleton()	{return msSingleton;}
		private:
			enum VertexBuffer
			{
				VbProjection = 0,
				VbWorld,
				VbView,
				VbCount
			};

			enum RenderingState
			{
				RsNone = -1,
				RsSprite,
				RsSpotLight,
				RsCount
			};

			typedef std::vector<DxGraphicComponent*>	DxGraphicComponentArray;
			mutable RenderingState	mState;
			DxGraphicComponentArray	mSprites;
			ID3D11InputLayout*		mInputLayout;
			ID3D11VertexShader*		mVs;
			ID3D11PixelShader*		mPs[RsCount];
			ID3D11Buffer*			mVsBuffers[VbCount];
			ID3D11Buffer*			mPsBuffers[RsCount];
			static SpriteRenderer*	msSingleton;
			ID3D11SamplerState*		mSampler;
			//ID3D11RasterizerState*	mRasterizer;

			SpriteRenderer(ID3D11Device *device, const Size &screenSize);
			DxGraphicComponentArray::iterator findLocation(const DxGraphicComponent *s) const;
			ID3D10Blob* compileShader(const char *fileName, const char *entry, const char *profile) const;

			void setDc(ID3D11DeviceContext *ic, const RenderingState newState) const;
			void _render(ID3D11DeviceContext *ic, const DxGraphicComponent *gc) const;
			ID3D11Buffer* createProjectionBuffer(ID3D11Device *device, const Size &screenSize) const;
			ID3D11Buffer* createMutableBuffer(ID3D11Device *device, const UINT size) const;
			void setBuffer(ID3D11DeviceContext *ic, ID3D11Buffer *buffer, const float *data) const;
			void setViewMatrix(ID3D11DeviceContext *ic, ID3D11Buffer *buffer) const;
			void setWorldMatrix(ID3D11DeviceContext *ic, ID3D11Buffer *buffer, const DxGraphicComponent *s) const;
			void setShadingBuffer(ID3D11DeviceContext *ic, ID3D11Buffer *buffer, const DxSprite *s) const;
			void setLightingBuffer(ID3D11DeviceContext *ic, ID3D11Buffer *buffer, const DxSpotLight *sl) const;
			ID3D11SamplerState* createSampler(ID3D11Device *device) const;

			void render(ID3D11DeviceContext *ic, const DxSprite *sprite) const;
			void render(ID3D11DeviceContext *ic, const DxSpotLight *spotLight) const;

			friend class SpriteIterator;
		public:
			class SpriteIterator
			{
				public:
					inline SpriteIterator()
						: mIterator(SpriteRenderer::GetSingleton()->mSprites.begin())
						, mEnd(SpriteRenderer::GetSingleton()->mSprites.end())
					{
						if(hasMore() && !peek())
							next();
					}
					const DxSprite* peek() const;
					void next();
					bool hasMore() const;
				private:
					SpriteRenderer::DxGraphicComponentArray::const_iterator	mIterator;
					SpriteRenderer::DxGraphicComponentArray::const_iterator	mEnd;
			};
	};
}

#endif // __GlSpriteRenderer_h__
